package com.jz.main.io.websocketnetty;


import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.http.*;
import io.netty.handler.codec.http.websocketx.*;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
import io.netty.util.CharsetUtil;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URI;

 public class WebSocketClient {
    public static void main(String[] args) throws Exception {
        URI uri = new URI("ws://localhost:8080/websocket");
        String scheme = uri.getScheme() == null ? "ws" : uri.getScheme();
        final String host = uri.getHost() == null ? "127.0.0.1" : uri.getHost();
        final int port = uri.getPort() == -1 ? (scheme.equalsIgnoreCase("ws") ? 80 : 443) : uri.getPort();

        final boolean ssl = "wss".equalsIgnoreCase(scheme);
        final SslContext sslCtx;
        if (ssl) {
            sslCtx = SslContextBuilder.forClient()
                    .trustManager(InsecureTrustManagerFactory.INSTANCE).build();
        } else {
            sslCtx = null;
        }

        EventLoopGroup group = new NioEventLoopGroup();
        try {
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(group)
                    .channel(NioSocketChannel.class)
                    .handler(new NettyWebSocketClientInitializer(sslCtx, uri));
            BufferedReader userInput = new BufferedReader(new InputStreamReader(System.in));
            while (true){
                Channel channel = bootstrap.connect(uri.getHost(), port).sync().channel();
                NettyWebSocketClientHandler handler = (NettyWebSocketClientHandler) channel.pipeline().last();
                String message;
                while ((message = userInput.readLine()) != null) {
                    // Connect to the server
                    // Handshake
                    handler.handshakeFuture().sync();
                    if ("exit".equalsIgnoreCase(message)) {
                        break;
                    }
                    // Send a message
                    handler.sendTextMessage(message);

                    // Close the WebSocket connection
                    handler.close();

                    // Wait for the channel to close
                    channel.closeFuture().sync();
                    break;
                }
            }

//            1 , 3 , -2 , 3 ,-1, 4, 2
//        cur 1   4   -2   0  -1  4  6
//        max 1   4    4   4  4   4   5
//            // Send a message
//            String message = "Hello, WebSocket!";
//            handler.sendTextMessage(message);
//
//            // Close the WebSocket connection
//            handler.close();
//
//            // Wait for the channel to close
//            channel.closeFuture().sync();
        } finally {
            // Shut down the event loop group
//            group.shutdownGracefully();
        }
    }
}

class NettyWebSocketClientHandler extends SimpleChannelInboundHandler<Object> {
    private final WebSocketClientHandshaker handshaker;
    private ChannelPromise handshakeFuture;
    private Channel channel;

    public NettyWebSocketClientHandler(WebSocketClientHandshaker handshaker) {
        this.handshaker = handshaker;
    }

    public ChannelFuture handshakeFuture() {
        return handshakeFuture;
    }

    public void sendTextMessage(String message) {
        channel.writeAndFlush(new TextWebSocketFrame(message));
    }

    public void close() {
        channel.writeAndFlush(new CloseWebSocketFrame());
    }

    @Override
    public void handlerAdded(ChannelHandlerContext ctx) {
        handshakeFuture = ctx.newPromise();
        channel = ctx.channel();
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) {
        handshaker.handshake(ctx.channel());
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) {
        System.out.println("WebSocket客户端断开连接！");
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
        Channel ch = ctx.channel();
        if (!handshaker.isHandshakeComplete()) {
            try {
                handshaker.finishHandshake(ch, (FullHttpResponse) msg);
                System.out.println("WebSocket客户端连接成功！");
                handshakeFuture.setSuccess();
            } catch (Exception e) {
                System.out.println("WebSocket客户端连接失败！");
                handshakeFuture.setFailure(e);
            }
            return;
        }

        if (msg instanceof FullHttpResponse) {
            FullHttpResponse response = (FullHttpResponse) msg;
            throw new IllegalStateException("意外的HttpResponse (状态=" + response.status() + ", 内容=" + response.content().toString(CharsetUtil.UTF_8) + ')');
        }

        WebSocketFrame frame = (WebSocketFrame) msg;
        if (frame instanceof TextWebSocketFrame) {
            TextWebSocketFrame textFrame = (TextWebSocketFrame) frame;
            System.out.println("WebSocket客户端收到消息：" + textFrame.text());
        } else if(frame instanceof CloseWebSocketFrame){

        }else{
            throw new UnsupportedOperationException("不支持的WebSocketFrame类型：" + frame.getClass().getName());
        }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        cause.printStackTrace();
        if (!handshakeFuture.isDone()) {
            handshakeFuture.setFailure(cause);
        }
        ctx.close();
    }
}

class NettyWebSocketClientInitializer extends ChannelInitializer<Channel> {
    private final SslContext sslCtx;
    private final URI uri;

    public NettyWebSocketClientInitializer(SslContext sslCtx, URI uri) {
        this.sslCtx = sslCtx;
        this.uri = uri;
    }

    @Override
    protected void initChannel(Channel ch) {
        ChannelPipeline pipeline = ch.pipeline();
        if (sslCtx != null) {
            pipeline.addLast(sslCtx.newHandler(ch.alloc(), uri.getHost(), uri.getPort()));
        }
        pipeline.addLast(
                new HttpClientCodec(),
                new HttpObjectAggregator(8192),
                new NettyWebSocketClientHandler(
                        WebSocketClientHandshakerFactory.newHandshaker(
                                uri, WebSocketVersion.V13, null, false, new DefaultHttpHeaders(), 1280000)));
    }
}
